#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/fb.h>
#include <linux/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "sqlite3.h"
#include "head.h"

#define DEBUG

int fifo_in, fifo_out;
int fifo_SQLite_V4L2;
int fifo_SQLite_ALSA;
int fifo_SQLite_CARID;
char SQL[200];
char *err;
bool first =-true;

int sql_callback(void *arg, int len, char **col_val, char **col_name)
{
	// 1，检测库中是否有相应的卡号
	if(arg != NULL)
	{
		(*(int *)arg) = time(NULL) - atol(col_val[2]);
		return 0;
	}

	// 2，显示当前数据库的信息
	// 针对第一行记录调用本函数的时候，就显示标题
	if(first)
	{
		printf("\n");
		for(int i=0; i<len; i++)
		{
			printf("%s\t\t", col_name[i]);
		}
		printf("\n=====================");
		printf("=====================\n");
        first = false;
	}
	for(int i=0; i<len; i++)
	{
		printf("%s\t", col_val[i]);
	}
	printf("\n");

	return 0;
}

/**
 * @brief 处理入库数据
 * @param  arg              My Param doc
 * @return void* 
 */
void *car_in_thread(void *arg)
{
    sqlite3* db = (sqlite3 *)arg;
    char msg[200];
    char *carid;
    uint32_t id;
    int ret;
    int n;
    while(1)
    {
        // 以阻塞的方式读取id
        read(fifo_in, &id, sizeof(id));
        bzero(SQL, 200);
        // 判断卡片的合法性
        snprintf(SQL, 200, "SELECT * FROM carinfo WHERE 卡号='%u';", id);
        n = 0;
        ret = sqlite3_exec(db, SQL, sql_callback, (void *)&n, &err);
        if(ret != SQLITE_OK)
        {
            printf("执行[%s]失败: %s\n", SQL, *err);
            exit(0);
        }
        carid = calloc(1,9);
        if(n != 0)
        {
            //通知语音播放		
			bzero(msg,200);
            snprintf(msg,200,"您好，此卡已进场,请勿重刷。");
			write(fifo_SQLite_ALSA,msg,strlen(msg));
            continue;
        }
        else
        {
            // 通知拍照
			write(fifo_SQLite_V4L2, "a", 1);
            bzero(carid,9);
            //等待车牌识别程序发来的识别车牌号
			int n = read(fifo_SQLite_CARID,carid,9);
            //debug用 
            printf("成功从管道读出 %d 字节\n",n);
            printf("收到车牌识别程序发来的车牌号 ：%s\n",carid);
            //通知语音播放
            bzero(msg,200);
            snprintf(msg,200,"欢迎%s车主入场。", carid);
            write(fifo_SQLite_ALSA,msg,strlen(msg));
        }

        //把
        bzero(SQL, 200);
		snprintf(SQL, 200, "INSERT INTO carinfo VALUES('%u', '%s', '%ld');", id, carid , time(NULL));
		Sqlite3_exec(db, SQL, NULL, NULL, &err);

        // debug用查询当前数据库中的数据
		bzero(SQL, 200);
		snprintf(SQL, 200, "SELECT * FROM carinfo;");
		Sqlite3_exec(db, SQL, callback, NULL, &err);
		printf("=====================");
		printf("=====================\n");
		first = true;
    }
    pthread_exit(NULL);
}

/**
 * @brief 处理出库数据
 * @param  arg              My Param doc
 * @return void* 
 */
void *car_out_thread(void *arg)
{
    sqlite3* db = (sqlite3 *)arg;
    uint32_t id;
    int ret = 0;
    char msg[200];
    char *parking_fee;
    while(1)
    {
        read(fifo_out, &id, sizeof(id));
        // 判断卡片是否合法
		bzero(SQL, 200);
		snprintf(SQL, 200, "SELECT * FROM carinfo WHERE 卡号='%u';", id);
		int n = 0;
		ret = sqlite3_exec(db, SQL, callback, (void *)&n, &err);
        if(ret != SQLITE_OK)
        {
            printf("执行[%s]失败: %s\n", SQL, *err);
            exit(0);
        }
        if(n == 0)
        {
            //通知语音播放		
			bzero(msg,200);
			snprintf(msg,200,"您好，此卡未进场。");
			write(fifo_SQLite_ALSA,msg,strlen(msg));
			continue;
        }
        else
        {
            //根据停车时长计算停车费用
            parking_fee = calloc(1,20); 
            bzero(parking_fee,20);
            parking_fee = parking_cost(n); // 在callback函数中已经算出了这个n的值
            bzero(msg,200);
            snprintf(msg,200,"停车时长%d分，收费%s，一路平安。", n,parking_fee); // 发给音频处理管道，让他去处理
			write(fifo_SQLite_ALSA,msg,strlen(msg));
        }
        // 出库
        bzero(SQL, 200);
		snprintf(SQL, 200, "DELETE FROM carinfo WHERE 卡号='%u';", id);
		Sqlite3_exec(db, SQL, NULL, NULL, &err);

        // debug查询当前数据库中的数据
		bzero(SQL, 200);
		snprintf(SQL, 200, "SELECT * FROM carinfo;");
		Sqlite3_exec(db, SQL, callback, NULL, &err);
		printf("=====================");
		printf("=====================\n");
		first = true;
    }
    pthread_exit(NULL);
}

/**
 * @brief sqlite主进程
 * @param  argc             My Param doc
 * @param  argv             My Param doc
 * @return int 
 */
int main(int argc, char **argv)
{
    // 1，创建、打开一个数据库文件
    int ret;
	sqlite3 *db = NULL;
	ret = sqlite3_open_v2("parking.db", &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
	if(ret != SQLITE_OK)
	{
		printf("创建数据库文件失败: %s\n", sqlite3_errmsg(db));
		exit(0);
	}

    // 2.创建表
	bzero(SQL, 200);
        /* 创建一个数据库名字叫carinfo，包含3列，每列都是TEXT */
	snprintf(SQL, 200, "%s", "CREATE TABLE IF NOT EXISTS carinfo(卡号 TEXT, 车牌 TEXT, 入库时间 TEXT);");
	ret = sqlite3_exec(db, SQL, NULL, NULL, &err);
    if(ret != SQLITE_OK)
	{
		printf("执行[%s]失败: %s\n", SQL, *err);
		exit(0);
	}

    // 3.打开管道
	fifo_in  = open(RFID_SQLite_in,  O_RDWR);
	fifo_out = open(RFID_SQLite_out, O_RDWR);
	fifo_SQLite_V4L2 = open(SQLite_V4L2, O_RDWR);
	fifo_SQLite_ALSA = open(SQLite_ALSA, O_RDWR);
    fifo_SQLite_CARID = open(SQLite_CARID, O_RDWR);

    // 4.创建线程，等待入库卡号
    pthread_t tid_in,tid_out;
    pthread_create(&tid_in, NULL, car_in_thread, (void *)db);
    pthread_create(&tid_out, NULL, car_out_thread, (void *)db);

    // 5.等待线程结束
    pthread_join(tid_in, NULL);
    pthread_join(tid_out, NULL);

    // 6.进程结束，资源清理
    close(fifo_in);
    close(fifo_out);
    close(fifo_SQLite_V4L2);
    close(fifo_SQLite_ALSA);
    close(fifo_SQLite_CARID);
    sqlite3_close(db);
    exit(0);
}